/**
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "Endpoint.h"
#include "utils/json_parser.h"
#include "utils/logger.h"

namespace panda::es2panda::ls {

Endpoint::Endpoint(std::istream &in, std::ostream &out)
    : in_(in), out_(out) {
}

void Endpoint::HandleMessage(const std::string &message,MessageHandler &handler) {
    JsonObject request(message);
    if (!request.IsValid()) {
        LOG(ERROR, ES2PANDAD) << "Invalid request: " << message;
        return;
    }
    // ID
    auto id = request.GetValue<JsonObject::NumT>("id");
    // method
    auto method = request.GetValue<JsonObject::StringT>("method");
    if (method == nullptr) { // response
        if (id == nullptr) {
            LOG(ERROR, ES2PANDAD) << "No method & No id, msg:" << message;
            return;
        }
        auto error = request.GetValue<JsonObject::StringT>("error");
        if (error != nullptr) {
            // reply error message
            return;
        }

        auto result = request.GetValue<JsonObject::StringT>("result");
        if (result != nullptr) {
            // reply result
            // handler.reply(id, result)
            return;
        }
    }

    LOG(DEBUG, ES2PANDAD) << "id: " << *id << ",method: " << method->c_str();

    auto params = request.GetValue<JsonObject::JsonObjPointer>("params");
    if (params == nullptr) {
        // no params
        return;
    }

    if (id != nullptr) {
        handler.onCall(*method, **params, *id);
        return;
    }

    // handler.Notify(method, params);
}

std::string& Endpoint::Trim(std::string &line, std::string delimter) 
{
    if (line.empty()) {
        return line;
    }

    for (auto it : delimter) {
        line.erase(0, line.find_first_of(it));
        line.erase(line.find_last_not_of(it) + 1);
    }

    return line;
}

bool Endpoint::ReadMessage([[maybe_unused]] std::string &message)
{
    const std::string cl("Content-Length: ");
    //unsigned long long content_length = 0;
    std::streamsize content_length = 0;
    for (;;) {
        if (in_.eof() || in_.fail()) {
            // error happend
            return false;
        }
        std::string line;
        std::getline(in_, line);

        // ignore comments
        if (line.find('#') == 0) {
            continue;
        }

        if (line.find(cl)== 0) {
            if (content_length != 0) {
                // duplicate header
                // LOG(WARNNING, ES2PANDAD) << "duplicate header, discard previous one: " << content_length;
            }
            
            std::stringstream ss(line.substr(cl.size()));
            ss >> content_length;
        }

        if (Trim(line).empty()) {
            break;
        }
    }

    message.reserve(content_length);

    // read json rpc message;
    char buf[BUFSIZ] = {0};
    std::streamsize readn = 0;
    for (std::streamsize pos = 0; pos < content_length; pos+=readn) {
        in_.read(buf, content_length - pos);
        readn = in_.gcount();
        if (readn == 0) {
            return false;
        }
        message.append(buf, readn);
    }

    return true;
}

bool Endpoint::Loop(MessageHandler &handler) {
    while (!in_.eof()) {
        // read message
        std::string message;
        if (!ReadMessage(message)) {
            // error happend, exit
            LOG(ERROR, ES2PANDAD) << "fail to read message";
            return false;
        }

        HandleMessage(message, handler);
    }
    return true;
}

}
